<?php
/**
 *
 *
 * @author Zeon <scplay@gmail.com>
 * @date 2019/5/28 14:36
 * @copyright PanxSoft Inc.
 */

namespace App\Http\Controllers\WebView\V1;


use App\Http\Controllers\ApiController;
use App\Models\Coupon\CouponCard;
use App\Models\Coupon\CouponTicket;
use App\Models\User;
use App\Models\UserAction;
use App\Services\Utils\Guard;
use App\Services\Utils\Uuid;
use Illuminate\Http\Request;
use App\Services\Utils\Random;
use Illuminate\Database\Eloquent\Collection;

class CouponCardController extends ApiController
{
    const ROUTES = [
        'index'    => 'webview.v1.coupon-card.index',
        'store'    => 'webview.v1.coupon-card.store',
        'patchUse' => 'webview.v1.coupon-card.patchUse',
    ];

    const HUNDRED_PERCENT = 1;
    const DAILY_LOTTERY_COUNT = 3;

    /**
     *
     * @param Request $request
     *
     * @return array|\Illuminate\Http\Response
     */
    public function index(Request $request)
    {
        $user = Guard::user();

        $coupon_cards = $user
            ->couponCards()
            ->oldest()
            ->get();

        return $this->response(compact('coupon_cards'));
    }

    public function create()
    {
        /** @var User $user */
        $user = Guard::user();

        $sum_score = (int) $user->userScores()->sum('score');

        $lottery_remain_count = $this->getUserTodayLotteryRemainCount($user);

        $daily_coupon_cards = $this->getDailyCouponCards($user);

        return $this->response(compact('sum_score', 'lottery_remain_count', 'daily_coupon_cards'));
    }


    /**
     * @param Request $request
     *
     * @return array|\Illuminate\Http\Response
     * @throws \Exception
     */
    public function store(Request $request)
    {
        /** @var User $user */
        $user = Guard::user();

        $lottery_remain_count = $this->getUserTodayLotteryRemainCount($user);
            $this->checkCanOperate($lottery_remain_count > 0, 'daily lottery remain count limit');

        $coupon_tickets = (new CouponTicket)
            ->where('is_available', CouponTicket::IS_AVAILABLE)
            ->get();
            $this->checkItemExist($coupon_tickets->count(), 'coupon tickets');

        $probabilities = $this->formatTicketIdProbabilities($coupon_tickets);

        /** @var CouponTicket $coupon_ticket */
        $coupon_ticket = $coupon_tickets->find(Random::lottery($probabilities));
            $this->checkItemExist($coupon_ticket, 'lottery coupon ticket');

        $coupon_card = $this->createCouponCardByType($coupon_ticket);

        return $this->response(compact('coupon_card'));
    }

    /**
     * @param Collection $coupon_tickets
     *
     * @return array
     */
    private function formatTicketIdProbabilities(Collection $coupon_tickets)
    {
        $probability_sum = $coupon_tickets->sum('probability');
        if ($probability_sum < self::HUNDRED_PERCENT) { // 如果概率合小于 1 补空值为重新抽奖或谢谢
            $null_probability = self::HUNDRED_PERCENT - $probability_sum;
            $coupon_tickets->push(new CouponTicket([
                'code'        => null,
                'probability' => $null_probability,
            ]));
        }

        $probability_accumulated = 0;
        $length_cnt = 1;
        $ticket_length = $coupon_tickets->count();
        $format_probabilities = [];

        /** @var CouponTicket $coupon_ticket */
        foreach ($coupon_tickets as $coupon_ticket) {
            $percent = number_format($coupon_ticket->probability / $probability_sum,
                2, '.', '');

            $probability_accumulated += $percent;
            if ($length_cnt === $ticket_length) { // last index must be "1"
                $probability_accumulated = self::HUNDRED_PERCENT;
            }

            // php 数组的 key 必须是 string 类型才会变成字典，否则 float 会默认转成 int 作数组的整数 index
            $format_probabilities[(string) $probability_accumulated] = $coupon_ticket->getKey();
            $length_cnt += 1;
        }

        uksort($format_probabilities, function (float $a, float $b) {
            return $b < $a;
        }); // sort key asc

        return $format_probabilities;
    }

    /**
     * @param CouponTicket $coupon_ticket
     *
     * @return CouponCard
     */
    private function createCouponCardByType(CouponTicket $coupon_ticket): CouponCard
    {
        $user = Guard::user();

        /** @var CouponCard $coupon_card */
        $coupon_card = $user->couponCards()->create([
            'coupon_ticket_id' => $coupon_ticket->getKey(),
            'type'             => $coupon_ticket->type,
            'code'             => Uuid::generate(),
            'title'            => $coupon_ticket->title,
            'description'      => $coupon_ticket->description,
            'started_at'       => $coupon_ticket->started_at,
            'ended_at'         => $coupon_ticket->ended_at,
            'is_used'          => $coupon_ticket->isTypeScore()
                ? CouponCard::IS_USED
                : CouponCard::NOT_USED,
        ]);

        if ($coupon_ticket->isTypeScore()) {
            $user->userScores()->create([
                'score'           => $coupon_ticket->score,
                'sourceable_id'   => $coupon_ticket->getKey(),
                'sourceable_type' => class_basename($coupon_ticket),
                'remark'          => '中奖积分累积'
            ]);
        }

        return $coupon_card;
    }

    /**
     * @param User $user
     *
     * @return int
     */
    private function getUserTodayLotteryRemainCount(User $user): int
    {
        $today_card_count = $user->couponCards()
            ->whereDate('created_at', today())->count();

        $today_action_count = $user->userActions()
            ->where('type', UserAction::ADD_LOTTERY_COUNT_TYPES)
            ->whereDate('created_at', today())->count();

        $lottery_remain_count = self::DAILY_LOTTERY_COUNT - $today_card_count + $today_action_count;

        return $lottery_remain_count;
    }

    /**
     * @param $key
     *
     * @return array|\Illuminate\Http\Response
     * @throws \App\Exceptions\ApiAbstractException
     */
    public function patchUse($key)
    {
        /** @var User $user */
        $user = Guard::user();

        $coupon_card = $user->couponCards()
            ->where('is_used', CouponCard::NOT_USED)
            ->find($key);
            $this->checkItemExist($coupon_card, 'coupon tickets');

        $updated = $coupon_card->update(['is_used' =>CouponCard::IS_USED]);

        return $this->response(compact('updated'));
    }

    /**
     * @param User $user
     *
     * @return CouponCard[]|Collection|\Illuminate\Database\Query\Builder[]|\Illuminate\Support\Collection
     */
    private function getDailyCouponCards(User $user)
    {
        $coupon_cards = (new CouponCard)
            ->whereDate('created_at', today())
            ->where('user_id', '<>', $user->getKey())
            ->with('user')
            ->oldest()
            ->get();

        $coupon_cards->each(function (CouponCard $coupon_card) {
            $coupon_card->user->makeHidden('phone')->append('asterisk_phone');
        });

        return $coupon_cards;
    }
}